<template>
  <div class="cascade-selector">
    <div class="selector-container">
      <div
        class="cascade-main"
        @click="toggleDropdown"
        ref="cascadeMainRef"
      >
        <div class="selected-label" :class="{ placeholder: !hasSelected }">
          {{ displayLabel || placeholder }}
        </div>
        <GIcon
          name="gt-line-down"
          class="arrow"
          :class="{
            'arrow-active': isDropdownVisible,
          }"
          :style="isDropdownVisible ? { transform: 'rotate(180deg)' } : {}"
        />
      </div>

      <!-- 使用 Teleport 将下拉框挂载到 body -->
      <Teleport to="body">
        <transition name="fade-bottom">
          <div
            class="dropdown-container"
            v-show="isDropdownVisible"
            ref="dropdownRef"
            :style="dropdownStyle"
          >
            <div class="dropdown-panel">
              <div
                class="dropdown-menu"
                v-for="(menu, index) in menus"
                :key="index"
              >
                <div
                  class="dropdown-item"
                  v-for="item in menu"  
                  :key="item[valueCode]"
                  :class="{ active: isItemSelected(item, index) }"
                  @click="selectItem(item, index)"
                >
                  {{ item[labelCode] }}
                  <GIcon
                    class="arrow-right"
                    v-if="item[childrenCode] && item[childrenCode].length"
                    name="gt-chevron-right"
                  />
                </div>
              </div>
            </div>
          </div>
        </transition>
      </Teleport>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
import { onClickOutside } from "@vueuse/core";


interface CascadeOption {
  value: string;
  label: string;
  children?: CascadeOption[];
  [key: string]: any; // 添加索引签名
}

interface Props {
  modelValue?: string[];
  placeholder?: string;
  clearable?: boolean;
  customOptions?: CascadeOption[];
  valueCode?: string;
  labelCode?: string;
  childrenCode?: string;
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [],
  placeholder: "请选择",
  clearable: true,
  customOptions: undefined,
  valueCode: 'value',
  labelCode: 'label',
  childrenCode: 'children',
});

const emit = defineEmits<{
  (e: "update:modelValue", value: string[]): void;
  (e: "change", value: string[]): void;
}>();

// 组件状态
const options = ref<CascadeOption[]>([]);
const selectedOptions = ref<string[]>([]);
const selectedNodes = ref<CascadeOption[]>([]);
const isDropdownVisible = ref(false);
const menus = ref<CascadeOption[][]>([]);
const dropdownStyle = ref({
  position: "absolute",
  top: "0px",
  left: "0px",
});

// DOM 引用
const cascadeMainRef = ref<HTMLElement | null>(null);
const dropdownRef = ref<HTMLElement | null>(null);

// 计算属性
const hasSelected = computed(() => selectedOptions.value.length > 0);
const displayLabel = computed(() =>{
  return selectedNodes.value.map((item) => item[props.labelCode]).join(" / ")
});

// 监听属性变化
watch(
  () => props.modelValue,
  (val) => {
    if (val && Array.isArray(val)) {
      selectedOptions.value = [...val];
      updateSelectedNodes();
    } else {
      selectedOptions.value = [];
      selectedNodes.value = [];
    }
  },
  { immediate: true }
);

watch(
  () => props.customOptions,
  (val) => {
    if (val && val.length > 0) {
      options.value = val;
      initMenus();
      updateSelectedNodes();
    }
  },
  { immediate: true }
);

// 监听下拉框显示状态变化
watch(isDropdownVisible, (visible) => {
  if (visible) {
    nextTick(() => {
      updateDropdownPosition();
      // 添加窗口大小变化监听
      window.addEventListener("resize", updateDropdownPosition);
      // 添加滚动监听
      window.addEventListener("scroll", updateDropdownPosition, true);
    });
  } else {
    // 移除监听
    window.removeEventListener("resize", updateDropdownPosition);
    window.removeEventListener("scroll", updateDropdownPosition, true);
  }
});

// 生命周期钩子
onMounted(() => {
  onClickOutside(
    dropdownRef,
    () => {
      isDropdownVisible.value = false;
    },
    { ignore: [cascadeMainRef] }
  );
});

onBeforeUnmount(() => {
  window.removeEventListener("resize", updateDropdownPosition);
  window.removeEventListener("scroll", updateDropdownPosition, true);
});

// 更新下拉框位置
function updateDropdownPosition() {
  if (!cascadeMainRef.value || !dropdownRef.value) return;

  const triggerRect = cascadeMainRef.value.getBoundingClientRect();

  // 计算下拉框的位置 - 修正计算方式
  const top = `${triggerRect.bottom}px`;
  const left = `${triggerRect.left}px`;

  // 更新样式
  dropdownStyle.value = {
    position: "fixed",
    top,
    left,
  };
}

function initMenus() {
  menus.value = [options.value];
}

function toggleDropdown() {
  isDropdownVisible.value = !isDropdownVisible.value;
  if (isDropdownVisible.value) {
    initMenus();
    updateMenusBySelectedOptions();
  }
}

function selectItem(item: CascadeOption, menuIndex: number) {
  // 检查是否点击了已选中的最后一级项目
  const isClickingSameItem = menuIndex === selectedOptions.value.length - 1 && selectedOptions.value[menuIndex] === item[props.valueCode];
  
  // 更新选中的选项
  selectedOptions.value = selectedOptions.value.slice(0, menuIndex);
  selectedOptions.value.push(item[props.valueCode]);

  // 如果有子菜单，则显示子菜单
  if (item[props.childrenCode] && item[props.childrenCode].length) {
    menus.value = menus.value.slice(0, menuIndex + 1);
    menus.value.push(item[props.childrenCode]);
  } else {
    // 如果点击的是已选择的最后一级项目，则取消选择
    if (isClickingSameItem) {
      selectedOptions.value = [];
    }
    isDropdownVisible.value = false;
    updateSelectedNodes();
    emit("update:modelValue", selectedOptions.value);
    emit("change", selectedOptions.value);
  }
}

function isItemSelected(item: CascadeOption, menuIndex: number) {
  return selectedOptions.value[menuIndex] === item[props.valueCode];
}

function clearSelection(e: Event) {
  e.stopPropagation();
  selectedOptions.value = [];
  selectedNodes.value = [];
  emit("update:modelValue", []);
  emit("change", []);
}

function updateSelectedNodes() {
  if (!selectedOptions.value.length) {
    selectedNodes.value = [];
    return;
  }

  selectedNodes.value = [];
  let currentOptions = options.value;

  for (let i = 0; i < selectedOptions.value.length; i++) {
    const value = selectedOptions.value[i];
    const found = currentOptions.find((item) => item[props.valueCode] === value);

    if (found) {
      selectedNodes.value.push(found);
      if (found[props.childrenCode]) {
        currentOptions = found[props.childrenCode];
      } else {
        break;
      }
    } else {
      break;
    }
  }

}

function updateMenusBySelectedOptions() {
  if (!selectedOptions.value.length) return;

  menus.value = [options.value];
  let currentOptions = options.value;

  for (let i = 0; i < selectedOptions.value.length; i++) {
    const value = selectedOptions.value[i];
    const found = currentOptions.find((item) => item[props.valueCode] === value);

    if (found && found[props.childrenCode] && found[props.childrenCode].length) {
      menus.value.push(found[props.childrenCode]);
      currentOptions = found[props.childrenCode];
    } else {
      break;
    }
  }
}
</script>

<style scoped>
.cascade-selector {
  position: relative;
  font-size: 14px;
}

.cascade-main {
  position: relative;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  cursor: pointer;
  transition: border-color 0.3s
      var(
        --devui-animation-ease-in-out-smooth,
        cubic-bezier(0.645, 0.045, 0.355, 1)
      ),
    box-shadow var(--devui-animation-duration-base, 0.2s)
      var(--devui-animation-ease-in, cubic-bezier(0.5, 0, 0.84, 0.25));
}

.selected-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.placeholder {
  color: var(--theme-placeholder);
}

.arrow {
  width: 16px;
  height: 16px;
  transition: transform 0.25s ease-out;
  display: inline-block;
  margin-left: 4px;
}

.arrow-active {
  transform: rotate(180deg);
}

.clear {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--theme-placeholder);
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  display: none;
}

.cascade-main:hover .clear {
  display: block;
}

.dropdown-container {
  position: fixed;
  margin-top: 5px;
  z-index: 1000;
  box-sizing: border-box;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  transform-origin: center top;
  border-radius: var(--devui-border-radius, 2px);
  background-color: var(--devui-connected-overlay-bg, #ffffff);
  box-shadow: var(--devui-shadow-length-connected-overlay, 0 2px 12px 0)
    var(--devui-shadow, rgba(37, 43, 58, 0.24));
  z-index: 1000;
}

.dropdown-panel {
  display: flex;
  max-height: 300px;
}

.dropdown-menu {
  min-width: 160px;
  max-height: 300px;
  overflow-y: auto;
  padding: 4px;
}

.dropdown-item {
  padding: 8px 12px;
  line-height: 1.5;
  cursor: pointer;
  position: relative;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 4px;
  border-radius: 2px;
}

.dropdown-item:hover {
  background-color: var(--devui-list-item-active-bg, #f2f5fc);
}

.dropdown-item.active {
  /* color: var(--theme-link); */
  background-color: var(--devui-list-item-active-bg, #f2f5fc);
}

.arrow-right {
  position: absolute;
  right: 15px;
  font-size: 16px;
  margin-top: 2px;
}

.devui-input--focus {
  box-shadow: 0 0 0 4px
    var(--devui-form-control-interactive-outline, rgba(94, 124, 224, 0.08));
  border-color: var(--devui-form-control-line-active, #5e7ce0);
}

.fade-bottom-enter-from,
.fade-bottom-leave-to {
  opacity: 0.8;
  transform: scaleY(0.8) translateY(-4px);
}

.fade-bottom-enter-to,
.fade-bottom-leave-from {
  opacity: 1;
  transform: scaleY(0.9999) translateY(0);
}

.fade-bottom-enter-active {
  transition: transform 0.2s cubic-bezier(0.16, 0.75, 0.5, 1),
    opacity 0.2s cubic-bezier(0.16, 0.75, 0.5, 1);
}

.fade-bottom-leave-active {
  transition: transform 0.2s cubic-bezier(0.5, 0, 0.84, 0.25),
    opacity 0.2s cubic-bezier(0.5, 0, 0.84, 0.25);
}
.dropdown-menu::-webkit-scrollbar {
  width: 6px;
  height: 4px;
}
</style>
